package com.winter.base;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.Set;

/**
 * @author JinHao
 * Date: 2023/07/25 11:26
 */
@Slf4j
@Component
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 未知异常处理
     *
     * @param e 异常
     * @return {@link WinterResponse}
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public WinterResponse<?> exceptionHandler(Exception e) {
        log.error("SystemException[系统异常] e:{}", e.getMessage(), e);
        return WinterResponse.error(e.getMessage());
    }

    /**
     * 自定义业务异常处理
     *
     * @param e 业务异常
     * @return {@link WinterResponse}
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public WinterResponse<?> exceptionHandler(BusinessException e) {
        log.error("BusinessException[业务异常] e:{}", e.getMessage(), e);
        return new WinterResponse<>(e.getCode(), e.getMessage());
    }

    /**
     * 单个参数校验 例如 @NotBlank(message = "姓名不能为空") String name;
     * 必须同时在索要实现方法参数校验的类上面添加注解 @Validated
     *
     * @param e 参数异常
     * @return {@link WinterResponse}
     */
    @ExceptionHandler(ValidationException.class)
    @ResponseBody
    public WinterResponse<?> exceptionHandler(ValidationException e) {
        log.error("ValidationException[参数异常] e:{}", e.getMessage(), e);
        StringBuilder errorMessage = new StringBuilder();
        if (e instanceof ConstraintViolationException) {
            ConstraintViolationException cve = (ConstraintViolationException) e;
            Set<ConstraintViolation<?>> violationSet = cve.getConstraintViolations();
            violationSet.forEach(item -> errorMessage.append(item.getMessage()));
        }
        return new WinterResponse<>(BusinessCode.ERROR_PARAMS.getCode(), errorMessage.toString());
    }

    /**
     * 处理JSON格式的对象参数异常 @RequestBody修饰
     * 1 @Validated 可以用在类型、方法和方法参数上。但是不能用在成员属性（字段）上, 可用于分组校验
     * 2 @Valid 可以用在方法、构造函数、方法参数和成员属性（字段）上, 可用于嵌套校验
     *
     * @param e 参数异常
     * @return {@link WinterResponse}
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public WinterResponse<?> exceptionHandler(MethodArgumentNotValidException e) {
        log.error("MethodArgumentNotValidException[参数错误] e:{}", e.getMessage(), e);
        return new WinterResponse<>(BusinessCode.ERROR_PARAMS.getCode(),
                e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
    }

    /**
     * 处理参数绑定异常
     *
     * @param e 绑定异常
     * @return {@link WinterResponse}
     */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public WinterResponse<?> exceptionHandler(BindException e) {
        log.error("BindException[绑定异常] e:{}", e.getMessage(), e);
        return new WinterResponse<>(BusinessCode.ERROR_PARAMS.getCode(),
                e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
    }

    /**
     * 处理参数不合法异常
     *
     * @param e 参数不合法
     * @return {@link WinterResponse}
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseBody
    public WinterResponse<?> exceptionHandler(IllegalArgumentException e) {
        log.error("IllegalArgumentException[参数不合法] e:{}", e.getMessage(), e);
        return new WinterResponse<>(BusinessCode.ERROR_PARAMS.getCode(), e.getMessage());
    }

    /**
     * 处理参数类型转换异常
     *
     * @param e 转换异常
     * @return {@link WinterResponse}
     */
    @ExceptionHandler(HttpMessageConversionException.class)
    @ResponseBody
    public WinterResponse<?> exceptionHandler(HttpMessageConversionException e) {
        log.error("HttpMessageConversionException[参数类型转换异常] e:{}", e.getMessage(), e);
        return new WinterResponse<>(BusinessCode.ERROR_PARAMS.getCode(), "参数格式错误");
    }
}
